#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <mp4v2/mp4v2.h>
#include "maz/assert.h"

int dlvl = DINF;

#define MAZ_FUNC_SPLIT          0

#define MAZ_STREAM_TYPE_AUDIO   0
#define MAZ_STREAM_TYPE_VIDEO   1

typedef struct _HEADER_
{
    char        sync[4];            // H3C_
    uint32_t    type;               // 码流类型 0:音频;  1:视频
    uint32_t    pack;               // 封装类型 0:ES;    1:PS
    uint32_t    atype;              // 音频编码 0:PCM;   1:G711A; 2:G711U; 3:G726_16; 4:G726_24; 5:G726_32; 6:G726_40; 7:AAC_LC
    uint32_t    vtype;              // 视频编码 0:H.264; 1:H.265
    uint32_t    ftype;              // 帧类型   0:I;     1:P;     2:B
    uint32_t    chan;               // 通道号
    uint32_t    len;                // 长度
    uint64_t    pts;                // 时间戳
    uint32_t    reserve[2];
} HEADER;

int main(int argc, char *argv[])
{
    int ret = -1;

    int rcnt = 0;                   // read count
    int wcnt = 0;                   // write count
    int scnt = 0;                   // stream count
    int len = 0;
    char *nalu = NULL;

    char *iname = NULL;
    char *oname = "x264.mp4";
#if MAZ_FUNC_SPLIT
    char *aname = "a.aac";
    char *vname = "v.h264";
    FILE *afile = NULL;             // audio input file
    FILE *vfile = NULL;             // video input file
#endif

    FILE *bfile = NULL;             // bin  output file
    uint8_t *buf = NULL;
    HEADER hd = { 0 };

    MP4FileHandle hdl = NULL;           // handle
    MP4TrackId vid = 0;                 // video track id
    MP4TrackId aid = 0;             // audio track id
    int width = 640;
    int height = 480;
    int framerate = 25;
    int timescale = 90000;
    int addStream = 1;
    uint8_t tmp[16] = { 0 };
    uint8_t conf[2] = { 0 };

    if (argc != 2)
    {
        dlog(DERR, "usage: %s <bin>\n", argv[0]);
        return -1;
    }

    iname = argv[1];

    // 打开输入bin文件
    bfile = fopen(iname, "rb");
    MAZASSERT_RETVAL(!bfile, -1, "err: fopen %s", iname);

#if MAZ_FUNC_SPLIT
    // 创建音频和视频文件
    afile = fopen(aname, "wb");
    MAZASSERT_RETVAL(!afile, -1, "err: fopen %s", aname);

    vfile = fopen(vname, "wb");
    MAZASSERT_RETVAL(!vfile, -1, "err: fopen %s", vname);
#endif

    // 申请缓存
    buf = malloc(10 * 1024 * 1024);

    hdl = MP4Create(oname, 0);
    MAZASSERT_RETVAL(MP4_INVALID_FILE_HANDLE == hdl, -1, "err: MP4Create");
    MP4SetTimeScale(hdl, timescale);

    aid = MP4AddAudioTrack(hdl, 16000, 1024, MP4_MPEG2_AAC_LC_AUDIO_TYPE);
    MAZASSERT_RETVAL(MP4_INVALID_TRACK_ID == aid , -1, "err: MP4AddAudioTrack");
    MP4SetAudioProfileLevel(hdl, 0x7F);

    /*
        首先, config有2个字节组成，共16位，具体含义如下：
        5 bits | 4 bits | 4 bits | 3 bits
        第一个 第二个 第三个 第四个

        // AAC object types
        #define MAIN 1
        #define LOW  2
        #define SSR  3
        #define LTP  4

        第一个：AAC Object Type
        第二个：Sample Rate Index
        第三个：Channel Number
        第四个：Don't care, 设 0
    */
    tmp[0] = 2;
    tmp[1] = 0x8;   // 16Khz
    tmp[2] = 1;
    conf[0] = ((tmp[0] << 3) & 0xF8) | ((tmp[1] >> 1) & 0x7);
    conf[1] = ((tmp[1] << 7) & 0x80) | ((tmp[2] << 3) & 0x78);
    MP4SetTrackESConfiguration(hdl, aid, conf, 2);

    // 循环读取文件
    while(1)
    {
        // 读取码流信息头
        memset(&hd, 0, sizeof(HEADER));
        rcnt = fread(&hd, 1, sizeof(HEADER), bfile);
        MAZASSERT_BRK_NOMSG(rcnt != sizeof(HEADER));

        // 判断码流同步码
        MAZASSERT_BRK(hd.sync[0] != 'H' || hd.sync[1] != '3' || hd.sync[2] != 'C' || hd.sync[3] != '_', "err: sync = %s", hd.sync);

        // 读取bin文件, 取出一帧码流数据
        rcnt = fread(buf, 1, hd.len, bfile);
        MAZASSERT_BRK(rcnt != hd.len, "err: read %s frame failed", iname);
        scnt++;

        dmsg(DINF, "num:%04d type:%d len:%d\r\n", scnt, hd.type, hd.len);

        if(hd.type == MAZ_STREAM_TYPE_AUDIO)
        {
#if MAZ_FUNC_SPLIT
            wcnt = fwrite(buf, 1, hd.len, afile);
            MAZASSERT_BRK(wcnt != hd.len, "err: fwrite %s audio frame header", aname);
#endif
            MP4WriteSample(hdl, aid, buf + 7, hd.len - 7, 1024, 0, 1);
        }
        else if(hd.type == MAZ_STREAM_TYPE_VIDEO)
        {
#if MAZ_FUNC_SPLIT
            wcnt = fwrite(buf, 1, hd.len, vfile);
            MAZASSERT_BRK(wcnt != hd.len, "err: fwrite %s video frame header", vname);
#endif

            switch (buf[4] & 0x1F)
            {
            case 0x07: // SPS + PPS + IDR
                if (addStream)
                {
                    vid = MP4AddH264VideoTrack(hdl,
                                               timescale,               // timescale
                                               timescale / framerate,   // timescale / framerate
                                               width,                   // width
                                               height,                  // height
                                               buf[5],                  // sps[1] AVCProfileIndication
                                               buf[6],                  // sps[2] profile_compat
                                               buf[7],                  // sps[3] AVCLevelIndication
                                               3);                      // 4 bytes length before each NAL unit
                    MAZASSERT_RETVAL(MP4_INVALID_TRACK_ID == vid, -1, "err: MP4AddH264VideoTrack");
                    MP4SetVideoProfileLevel(hdl, 0x7F);
                    addStream = 0;
                }
                MP4AddH264SequenceParameterSet(hdl, vid, buf + 4, 22 - 4);

                // 由于 SStar 编码后的I帧码流前将 SPS 和 PPS 包含在 I 帧前面, 因此这里集中处理
                // SPS 长度 22、PPS 长度 8
                MP4AddH264PictureParameterSet(hdl, vid, buf + 22 + 4, 8 - 4);
                len = hd.len - 22 - 8;
                nalu = buf + 22 + 8;
                nalu[0] = ((len - 4) >> 24) & 0xFF;
                nalu[1] = ((len - 4) >> 16) & 0xFF;
                nalu[2] = ((len - 4) >> 8) & 0xFF;
                nalu[3] = ((len - 4) >> 0) & 0xFF;
                MP4WriteSample(hdl, vid, nalu, len, MP4_INVALID_DURATION, 0, 1);
                break;
            default:
                buf[0] = ((hd.len - 4) >> 24) & 0xFF;
                buf[1] = ((hd.len - 4) >> 16) & 0xFF;
                buf[2] = ((hd.len - 4) >> 8) & 0xFF;
                buf[3] = ((hd.len - 4) >> 0) & 0xFF;
                MP4WriteSample(hdl, vid, buf, hd.len, MP4_INVALID_DURATION, 0, 0);
                break;
            }
        }
    }

    // 关闭文件
#if MAZ_FUNC_SPLIT
    fclose(afile);
    fclose(vfile);
#endif
    fclose(bfile);

    MP4Close(hdl, 0);

    return 0;
}
